﻿using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.CSharp;

namespace RuntimeHelloWorlds
{
	/// <summary>
	/// Uses the C# CodeDom (code document object model) to write the Hello, World 
	/// program, compile it and then invokes it via reflection.
	/// </summary>
	public class CSharpCodeDomHelloWorld
	{
		public void SayHelloWorld()
		{
			CodeNamespace helloNamespace = new CodeNamespace("CodeDomSampleNS");
			helloNamespace.Imports.Add(new CodeNamespaceImport("System"));

			CodeTypeDeclaration typeDeclaration = new CodeTypeDeclaration("CSharpCodeDomHelloWorldClass");
			helloNamespace.Types.Add(typeDeclaration);
			typeDeclaration.Attributes = MemberAttributes.Public;

			CodeMemberMethod method = new CodeMemberMethod();

			typeDeclaration.Members.Add(method);
			CodeMethodInvokeExpression consoleWriteLine = new CodeMethodInvokeExpression(
				new CodeTypeReferenceExpression("System.Console"),
				"WriteLine", new CodePrimitiveExpression("Hello, World! (CodeDom CSharp)"));

			method.Name = "HelloWorld";
			method.Attributes = MemberAttributes.Public;
			method.Statements.Add(consoleWriteLine);

			CodeCompileUnit compileUnit = new CodeCompileUnit();
			compileUnit.Namespaces.Add(helloNamespace);

			CompilerParameters parameters = new CompilerParameters();
			parameters.OutputAssembly = "HelloWorldCodeDomAssembly.dll";
			parameters.GenerateInMemory = true;
			parameters.GenerateExecutable = false;

			CSharpCodeProvider provider = new CSharpCodeProvider();

			StringWriter writer = new StringWriter();

			CodeGeneratorOptions options = new CodeGeneratorOptions();
			provider.GenerateCodeFromCompileUnit(compileUnit, writer, options);

			CompilerResults results = provider.CompileAssemblyFromDom(parameters, compileUnit);

			Type type = results.CompiledAssembly.GetTypes().First();
			MethodInfo methodInfo = type.GetMethods().Where(m => m.Name.Contains("HelloWorld")).First();
			Object obj = Activator.CreateInstance(type);
			methodInfo.Invoke(obj, null);

		}
	}
}
